/*
 * Transform.cpp
 *
 *  Created on: 1-dec.-2013
 *      Author: Axel
 */

#include <Transform.h>
#include "extraMath.h"

#include <iostream>

void Transform::InitScaleTransform(Matrix4f& m) const {
    m.m[0][0] = m_scale.x; m.m[0][1] = 0.0f     ; m.m[0][2] = 0.0f     ; m.m[0][3] = 0.0f;
    m.m[1][0] = 0.0f     ; m.m[1][1] = m_scale.y; m.m[1][2] = 0.0f     ; m.m[1][3] = 0.0f;
    m.m[2][0] = 0.0f     ; m.m[2][1] = 0.0f     ; m.m[2][2] = m_scale.z; m.m[2][3] = 0.0f;
    m.m[3][0] = 0.0f     ; m.m[3][1] = 0.0f     ; m.m[3][2] = 0.0f     ; m.m[3][3] = 1.0f;
}

void Transform::InitRotateTransform(Matrix4f& m) const {
    Matrix4f rx, ry, rz;

    const float x = ToRadian(m_rotateInfo.x);
    const float y = ToRadian(m_rotateInfo.y);
    const float z = ToRadian(m_rotateInfo.z);

    rx.m[0][0] = 1.0f; rx.m[0][1] = 0.0f   ; rx.m[0][2] = 0.0f    ; rx.m[0][3] = 0.0f;
    rx.m[1][0] = 0.0f; rx.m[1][1] = math::cos_axel(x); rx.m[1][2] = math::sin_axel(x); rx.m[1][3] = 0.0f;
    rx.m[2][0] = 0.0f; rx.m[2][1] = -math::sin_axel(x); rx.m[2][2] = math::cos_axel(x) ; rx.m[2][3] = 0.0f;
    rx.m[3][0] = 0.0f; rx.m[3][1] = 0.0f   ; rx.m[3][2] = 0.0f    ; rx.m[3][3] = 1.0f;

    ry.m[0][0] = math::cos_axel(y); ry.m[0][1] = 0.0f; ry.m[0][2] = -math::sin_axel(y); ry.m[0][3] = 0.0f;
    ry.m[1][0] = 0.0f   ; ry.m[1][1] = 1.0f; ry.m[1][2] = 0.0f    ; ry.m[1][3] = 0.0f;
    ry.m[2][0] = math::sin_axel(y); ry.m[2][1] = 0.0f; ry.m[2][2] = math::cos_axel(y) ; ry.m[2][3] = 0.0f;
    ry.m[3][0] = 0.0f   ; ry.m[3][1] = 0.0f; ry.m[3][2] = 0.0f    ; ry.m[3][3] = 1.0f;

    rz.m[0][0] = math::cos_axel(z); rz.m[0][1] = -math::sin_axel(z); rz.m[0][2] = 0.0f; rz.m[0][3] = 0.0f;
    rz.m[1][0] = math::sin_axel(z); rz.m[1][1] = math::cos_axel(z) ; rz.m[1][2] = 0.0f; rz.m[1][3] = 0.0f;
    rz.m[2][0] = 0.0f   ; rz.m[2][1] = 0.0f    ; rz.m[2][2] = 1.0f; rz.m[2][3] = 0.0f;
    rz.m[3][0] = 0.0f   ; rz.m[3][1] = 0.0f    ; rz.m[3][2] = 0.0f; rz.m[3][3] = 1.0f;

    m = rz * ry * rx;
}

void Transform::InitTranslationTransform(Matrix4f& m) const {
    m.m[0][0] = 1.0f; 			m.m[0][1] = 0.0f; 			m.m[0][2] = 0.0f; 			m.m[0][3] = 0.0f;
    m.m[1][0] = 0.0f; 			m.m[1][1] = 1.0f; 			m.m[1][2] = 0.0f; 			m.m[1][3] = 0.0f;
    m.m[2][0] = 0.0f; 			m.m[2][1] = 0.0f; 			m.m[2][2] = 1.0f; 			m.m[2][3] = 0.0f;
    m.m[3][0] =  m_worldPos.x; 	m.m[3][1] = m_worldPos.y; 	m.m[3][2] =  m_worldPos.z; 	m.m[3][3] = 1.0f;
}

const Matrix4f* Transform::GetTrans() {
    Matrix4f ScaleTrans, RotateTrans, TranslationTrans, PersProjTrans;

    InitScaleTransform(ScaleTrans);
    InitRotateTransform(RotateTrans);
    InitTranslationTransform(TranslationTrans);
    InitPerspectiveProj(PersProjTrans);

    m_transformation = PersProjTrans * TranslationTrans * RotateTrans * ScaleTrans;

    return &m_transformation;
}

void Transform::InitPerspectiveProj(Matrix4f& m) const {
    const float ar         = m_persProj.Width / m_persProj.Height;
    const float zNear      = m_persProj.zNear;
    const float zFar       = m_persProj.zFar;
    const float zRange     = zNear - zFar;
    const float tanHalfFOV = math::tan_axel(ToRadian(m_persProj.FOV / 2.0f));
    /*
    m.m[0][0] = 1.0f/(tanHalfFOV * ar); m.m[0][1] = 0.0f;            m.m[0][2] = 0.0f;                   m.m[0][3] = 0.0;
    m.m[1][0] = 0.0f;                   m.m[1][1] = 1.0f/tanHalfFOV; m.m[1][2] = 0.0f;                   m.m[1][3] = 0.0;
    m.m[2][0] = 0.0f;                   m.m[2][1] = 0.0f;            m.m[2][2] = (-zNear -zFar)/zRange ; m.m[2][3] = 2.0f * zFar*zNear/zRange;
    m.m[3][0] = 0.0f;                   m.m[3][1] = 0.0f;            m.m[3][2] = 1.0f;                   m.m[3][3] = 0.0;
	*/
    m.m[0][0] = 1.0f/(tanHalfFOV * ar); m.m[0][1] = 0.0f;            m.m[0][2] = 0.0f;                   m.m[0][3] = 0.0;
        m.m[1][0] = 0.0f;                   m.m[1][1] = 1.0f/(tanHalfFOV); m.m[1][2] = 0.0f;                   m.m[1][3] = 0.0;
        m.m[2][0] = 0.0f;                   m.m[2][1] = 0.0f;            m.m[2][2] = (zFar+zNear)/zRange ; m.m[2][3] = 0.0;
        m.m[3][0] = 0.0f;                   m.m[3][1] = 0.0f;            m.m[3][2] = 2*zFar*zNear/zRange;                   m.m[3][3] =1.0;

}

Matrix4f Transform::frustum(Matrix4f& m) {
	float right = m_persProj.right;
	float left = m_persProj.left;
	float top = m_persProj.top;
	float bottom = m_persProj.bottom;
	float n = m_persProj.zNear;
	float f = m_persProj.zFar;

	Matrix4f result;
	result.InitIdentity();

    if ((right == left) ||
        (top == bottom) ||
        (n == f) ||
        (n < 0.0) ||
        (f < 0.0))
       return result;

    result.m[0][0] = (2.0f * n) / (right - left);
    result.m[1][1] = (2.0f * n) / (top - bottom);

    result.m[2][0] = (right + left) / (right - left);
    result.m[2][1] = (top + bottom) / (top - bottom);
    result.m[2][2] = -(f + n) / (f - n);
    result.m[2][3]= -1.0f;

    result.m[3][2] = -(2.0f * f * n) / (f - n);
    result.m[3][3] =  0.0f;

    return result;
}

Matrix4f Transform::ortho(float left, float right, float bottom, float top, float n, float f) {
    return Matrix4f( Vector4f(2.0f / (right - left), 0.0f, 0.0f, 0.0f),
    		Vector4f(0.0f, 2.0f / (top - bottom), 0.0f, 0.0f),
    		Vector4f(0.0f, 0.0f, 2.0f / (n - f), 0.0f),
    		Vector4f((left + right) / (left - right), (bottom + top) / (bottom - top), (n + f) / (f - n), 1.0f) );
}

